#!/usr/bin/python3
# -*- coding: UTF-8 -*-

from tkinter import ttk
from tkinter import messagebox
import time
import tkinter as tk
import tkinter as tf
import threading
import serial
import serial.tools.list_ports

"""
功能介绍：初始化串口工具界面
参数[NULL]：NULL
"""
class UART_WINDOW:
    """
    功能介绍：初始化串口工具主界面
    参数[NULL]：NULL
    返回：无
    """
    def __init__(self):                 # 传入参数NULL
        self.serial = serial.Serial()   # 获得串口句柄
        self.baudrate = 115200          # 波特率
        self.comports = []              # 串口列表
        self.device = None              # 当前选中的串口号
        self.bitcheck = '无校验'         # 校验位
        self.bitdata = 8                # 数据位
        self.stopbit = 1                # 停止位
        self.interface()                # 界面初始化
        self.connection = False         # 串口是否连接标志位
        self.encoding = "gb2312"        # 字符编码格式
        self.search_comport()           # 启动查找一遍串口
        self.updata_comport()
        self.uart_main_thread = threading.Thread(target=self.uart_win_main_loop)         # 串口工具main loop线程初始化
        self.input_loop_thread = threading.Thread(target=self.uart_input_loop)           # 串口工具input loop线程初始化
        self.output_loop_thread = threading.Thread(target=self.uart_output_loop)         # 串口工具output loop线程初始化
        self.uart_main_thread.start()
        self.input_loop_thread.start()
        self.output_loop_thread.start()

        pass

    def interface(self):
        self.UART_WINDOW = tk.Tk()
        self.UART_WINDOW.title("Telink串口助手")
        self.UART_WINDOW.geometry("960x640")
        self.UART_WINDOW.config(bg='black')
        self.UART_WINDOW.iconbitmap("telink icon.ico")
        self.UART_WINDOW.minsize(0,0)
        self.UART_WINDOW.maxsize(0,0)
        #使用Frame在主界面上开个框，用来管理其他控件，大小同窗口一样，颜色默认是白色
        self.face = tk.Frame(self.UART_WINDOW)
        self.face.config(height=640, width=960)
        self.face.propagate(False)
        self.face.place(anchor='nw', x=0, y=0, height=640, width=960)

        # 开个控制串口工具的frame
        control_tool_frame = tk.Frame(self.face)
        control_tool_frame.config(height=640, width=250)
        control_tool_frame.propagate(False)
        control_tool_frame.place(anchor='nw', x=0, y=0, height=640, width=250)

        # 开个输出串口数据的frame
        output_uart_frame = tk.Frame(self.face)
        output_uart_frame.config(height=500, width=700)
        output_uart_frame.propagate(False)
        output_uart_frame.place(anchor='nw', x=250, y=0, height=500, width=710)

        # 开个输入串口数据的frame
        input_uart_frame = tk.Frame(self.face)
        input_uart_frame.config(height=140, width=700)
        input_uart_frame.propagate(False)
        input_uart_frame.place(anchor='nw', x=250, y=500, height=140, width=710)

        # 设置文本输入窗口
        self.input_uart_text = tk.Text(input_uart_frame, height=140, width=500, bg='white', fg="black")
        self.intput_uart_txt_scrollbar = tk.Scrollbar(input_uart_frame)
        # 设置这个是将文本和滚动条yscrollcommand关联，这样拉动滚动条的时候，文本就会移动，不设置则不会移动
        self.input_uart_text['yscrollcommand'] = self.intput_uart_txt_scrollbar.set
        # 设置这个是将文本和滚动条command关联，这样文本移动的时候，滚动条会停住，不设置则会立即弹到最上面
        self.intput_uart_txt_scrollbar['command'] = self.input_uart_text.yview
        self.input_uart_text.place(anchor='nw', x=0, y=0, height=140, width=710)
        self.intput_uart_txt_scrollbar.place(anchor='nw', x=690, y=0, height=140, width=20)

        # 设置串口输出窗口
        self.output_uart_text = tk.Text(output_uart_frame,height=10, width=10, bg='black', fg="#00FF00",font=('宋体',20))
        self.output_uart_text_scrollbar = tk.Scrollbar(output_uart_frame)
        self.output_uart_text['yscrollcommand'] = self.output_uart_text_scrollbar.set
        self.output_uart_text_scrollbar['command'] = self.output_uart_text.yview
        # 设置这个文本框不能输入
        self.output_uart_text.config(state=tk.DISABLED)
        self.output_uart_text.place(anchor='nw', x=0, y=0, height=500, width=710)
        self.output_uart_text_scrollbar.place(anchor='nw', x=690, y=0, height=500, width=20)


        # 设置端口号选择下拉窗口
        self.uart_port_combobox = ttk.Combobox(control_tool_frame)
        #self.uart_port_combobox.config(state=tk.DISABLED)
        self.uart_port_combobox.bind("<<ComboboxSelected>>", self.uart_port_combobox_callbacks)
        self.uart_port_combobox.place(anchor='nw', x=50, y=0, height=20, width=150)
        self.uart_port_label = tk.Label(control_tool_frame, text="端口号", width=5, height=1)
        self.uart_port_label.place(anchor='nw', x=0, y=0, height=20, width=50)

        # 设置波特率选择窗口
        self.uart_baudrate_combobox = ttk.Combobox(control_tool_frame)
        # self.uart_baudrate_combobox.config(state=tk.DISABLED)
        self.uart_baudrate_combobox.bind("<<ComboboxSelected>>", self.uart_baudrate_combobox_callbacks)
        self.uart_baudrate_combobox.place(anchor='nw', x=50, y=30, height=20, width=150)
        self.uart_baudrate_label = tk.Label(control_tool_frame, text="波特率", width=5, height=1)
        self.uart_baudrate_combobox['value'] = [1000000, 921600, 460800, 256000, 230400, \
                                        128000, 115200, 76800, 57600, 43000, 38400, 19200, 14400, \
                                        9600, 4800, 2400, 1200]
        # 默认选择6 即 115200波特率
        self.uart_baudrate_combobox.current(6)
        self.uart_baudrate_label.place(anchor='nw', x=0, y=30, height=20, width=50)


        # 设置校验位选择窗口
        self.uart_bitcheck_combobox = ttk.Combobox(control_tool_frame)
        # self.uart_bitcheck_combobox.config(state=tk.DISABLED)
        self.uart_bitcheck_combobox.bind("<<ComboboxSelected>>", self.serialselectcmd)
        self.uart_bitcheck_combobox.place(anchor='nw', x=50, y=60, height=20, width=150)
        self.uart_bitcheck_label = tk.Label(control_tool_frame, text="校验位", width=5, height=1)
        self.uart_bitcheck_combobox['value'] = ["无校验", "奇校验", "偶校验"]
        self.uart_bitcheck_combobox.current(0)
        self.uart_bitcheck_label.place(anchor='nw', x=0, y=60, height=20, width=50)

        # 设置数据位选择窗口
        self.uart_bitdata_combobox = ttk.Combobox(control_tool_frame)
        # self.uart_bitdata_combobox.config(state=tk.DISABLED)
        self.uart_bitdata_combobox.bind("<<ComboboxSelected>>", self.serialselectcmd)
        self.uart_bitdata_combobox.place(anchor='nw', x=50, y=90, height=20, width=150)
        self.uart_bitdata_label = tk.Label(control_tool_frame, text="数据位", width=5, height=1)
        self.uart_bitdata_combobox['value'] = [8, 7, 6, 5]
        self.uart_bitdata_combobox.current(0)
        self.uart_bitdata_label.place(anchor='nw', x=0, y=90, height=20, width=50)

        # 设置停止位选择窗口
        self.uart_stopbit_combobox = ttk.Combobox(control_tool_frame)
        # self.uart_stopbit_combobox.config(state=tk.DISABLED)
        self.uart_stopbit_combobox.bind("<<ComboboxSelected>>", self.serialselectcmd)
        self.uart_stopbit_combobox.place(anchor='nw', x=50, y=120, height=20, width=150)
        self.uart_stopbit_label = tk.Label(control_tool_frame, text="停止位", width=5, height=1)
        self.uart_stopbit_combobox['value'] = [1]
        self.uart_stopbit_combobox.current(0)
        self.uart_stopbit_label.place(anchor='nw', x=0, y=120, height=20, width=50)

        # 设置16进制显示按钮
        self.hex_show = tk.BooleanVar()
        self.hex_showcheck = tk.Checkbutton(control_tool_frame, text='十六进制显示', \
                                            onvalue=True, offvalue=False, variable=self.hex_show)
        self.hex_showcheck.place(anchor='nw', x=0, y=150)

        # 设置16进制发送按钮
        self.hex_send = tk.BooleanVar()
        self.hex_sendcheck = tk.Checkbutton(control_tool_frame, text='十六进制发送', \
                                           onvalue=True, offvalue=False, variable=self.hex_send)
        self.hex_sendcheck.place(anchor='nw', x=100, y=150)

        # 设置打开串口按钮
        self.open_uart_button = tk.Button(control_tool_frame, text='打开串口',width=20, height=1, \
                                          command=self.open_uart_button_callbacks)
        self.open_uart_button.place(anchor='nw', x=10, y=180)

        # 设置发送数据按钮
        self.send_uart_button = tk.Button(control_tool_frame, text='发送数据', width=20, height=1, \
                                          command=self.send_uart_button_callbacks)
        self.send_uart_button.place(anchor='nw', x=10, y=220)

        # 设置清除信息按钮
        self.clean_uart_button = tk.Button(control_tool_frame, text='清除信息', width=20, height=1, \
                                          command=self.null_callbacks)
        self.clean_uart_button.place(anchor='nw', x=10, y=260)

        pass

    def search_comport(self):
        #self.devices = []
        self.comports = list(serial.tools.list_ports.comports())
        #for comport in self.comports:
            #self.devices.append(comport.device)
            #pass
        pass

    def updata_comport(self):
        # 读一下串口列表长度，为0 就赋空值，要不然报错
        if len(list(serial.tools.list_ports.comports())) == 0:
            self.uart_port_combobox['value'] = [""]
            self.device = None
        else:
            self.uart_port_combobox['value'] = self.comports
            pass
        pass

    def uart_output_loop(self):
        while True:
            if self.connection:
                #if self.hex_show.get() == False:

                    #pass
                self.receive_data_len = self.serial.inWaiting()
                if self.receive_data_len != 0:
                    self.receive_data = ''.encode(self.encoding)
                    self.receive_data = self.receive_data + self.serial.read(self.receive_data_len)
                    self.output_uart_text.config(state=tk.NORMAL)
                    self.output_uart_text.insert(tk.END, self.receive_data.decode(self.encoding))
                    pass

                pass
            else:

                pass
            self.UART_WINDOW.update()
            pass
        pass

    def uart_input_loop(self):
        while True:
            if self.connection:
                self.input_data = self.input_uart_text.get(1.0, tk.END)
                self.input_data_len = len(self.input_data)
                if self.input_data_len != 1:
                    print("self.input_data", self.input_data)

                    pass

                pass
            else:

                pass
            self.UART_WINDOW.update()
        pass

    def uart_win_main_loop(self):
        while True:
            # 串口没连接情况下就不停的查找串口
            if self.connection == False:
                #print("uatr is no connection")
                self.search_comport()
                self.updata_comport()
            else:
                #print("uatr is connection")
                self.UART_WINDOW.update()
                pass

        pass
    # combobox的回调函数，入口参数需要2个，要不然报错
    def uart_port_combobox_callbacks(self, *args):
        # 读一下combobox列表长度，为0 就赋空值，要不然报错
        if len(list(self.uart_port_combobox.get())) == 0:
            print("device is null")
        else:
            self.device = self.uart_port_combobox.get().split()[0]
            print("device is ", self.device)
            pass

        pass

    def uart_baudrate_combobox_callbacks(self, *args):
        # 读一下combobox列表长度，为0 就赋空值，要不然报错
        if len(list(self.uart_baudrate_combobox.get())) == 0:
            print("baudrate is null")
        else:
            self.baudrate = self.uart_baudrate_combobox.get().split()[0]
            print("baudrate is ", self.baudrate)
            pass

        pass

    def serialselectcmd(self, *args):
        print('button is down')
        pass

    def close_uart(self):
        print(self.device + " is close !")
        self.connection = False
        self.serial.close()

        pass

    def open_uart(self):
        self.serial.port = self.device
        self.serial.baudrate = self.baudrate
        self.serial.timeout = 2
        try:
            self.serial.open()
        except Exception as error:
            tk.messagebox.showinfo(title='无法连接到串口', message=error)
            return False
        else:
            if self.serial.isOpen():
                print(self.device+" is open !")
                self.connection = True
                return True
            else:
                self.serial.close()
                self.connection = False
                return False

        pass
    def open_uart_button_callbacks(self):
        if self.open_uart_button['text'] == '打开串口':
            if self.serial.isOpen() == 0:
                self.open_uart()
                self.open_uart_button['text'] = '关闭串口'

                pass
        else:
            self.close_uart()
            self.open_uart_button['text'] = '打开串口'
            pass
        pass

    def send_uart_button_callbacks(self):
        if self.connection:
            if self.hex_send.get() == False:
                self.serial.write(self.input_data[0:-1].encode(self.encoding))
            else:
                hex_dict = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, \
                            '8': 8, '9': 9, 'A': 10, 'a': 10, 'B': 11, 'b': 11, 'C': 12, \
                            'c': 12, 'D': 13, 'd': 13, 'E': 14, 'e': 14, 'F': 15, 'f': 15}
                self.input_data = self.input_data.replace(' ', '')
                self.input_data = self.input_data.replace('\n', '')
                char = 0
                count = 0
                byte = b''
                for num in self.input_data:
                    try:
                        num = hex_dict[num]
                        pass
                    except:
                        tk.messagebox.showinfo(title='无法发送', message='输入错误')
                        return None
                    else:
                        count += 1
                        if count % 2 == 1:
                            char = num * 16
                            if count == len(self.input_data):
                                char = num
                                byte += bytes([char])
                                pass
                            pass
                        else:
                            char += num
                            byte += bytes([char])
                            pass
                        pass
                    pass
                self.serial.write(byte)
                pass
        else:
            tk.messagebox.showinfo(title='无法发送', message='请先打开串口')
            pass
        pass

    def null_callbacks(self):
        print("this is null callbacks")
        pass

    def run(self):
        print('uart_window mainloop is start')
        self.UART_WINDOW.mainloop()
        pass
